package faircat.android.exphone;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.widget.TextView;
import android.view.View;
import android.view.View.OnClickListener;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothServerSocket;
import java.util.UUID;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.widget.EditText;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import android.widget.ArrayAdapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.util.Log;
import java.util.LinkedList;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import android.os.Environment;
import android.widget.RadioGroup;
import android.widget.Button;


public class RfcommTestActivity extends Activity implements OnClickListener {
	static final int STATE_IDLE = 0;
	static final int STATE_LISTENING = 1;
	static final int STATE_CONNECTING = 2;
	static final int STATE_CONNECTED = 3;
	static final int STATE_DISCONNECTING = 4;
	
	int mState = STATE_IDLE;
	BluetoothAdapter mBtAdapter;
	BluetoothDevice mBtDevice;
	BluetoothSocket mBtSocket;
	BluetoothServerSocket mBtSrvSocket;
	UUID mUuid;
	int channel_id;
	String mRemoteAddr;
	RunThread mRunThread;
	RecordPut mRecordPut;
	boolean mAudioOutput = false;
	boolean mAudioTest = false;
	boolean mAudioSend = false;
	boolean mTextDisplay = false;
	//Track mTrack = null;

    int recv_count = 0;
    int track_count = 0;
    int record_count = 0;
    int send_count = 0;
    Timer mTimer;
    TimerTask mTimerTask = new TimerTask(){
    	public void run(){
    		sendMessage("recv_speed="+recv_count+"/"+track_count+"\n");
    		sendMessage("send_speed="+send_count+"/"+record_count+"\n");
    		recv_count = 0;
    		track_count = 0;
    		record_count = 0;
    		send_count = 0;
    	}
    };
    
    void sendMessage(String msg){
    	mHandler.obtainMessage(MSG_STRING, msg).sendToTarget();
    }
	
	List<String> mAddrList = new ArrayList<String>();
	String[] uuids = new String[]{
		"00001101-0000-1000-8000-00805F9B34FB",
		"00001103-0000-1000-8000-00805F9B34FB",
		"00001112-0000-1000-8000-00805F9B34FB",
		"0000111F-0000-1000-8000-00805F9B34FB"
	};
	
	File audioFile;
	DataOutputStream dos;
	RadioGroup mRadioGroup1;
	
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.rfcomm_test);
		
		mBtAdapter = BluetoothAdapter.getDefaultAdapter();
		mAudioOutput = false;
		mAudioTest = false;
		mAudioSend = false;
		
		findViewById(R.id.listen).setOnClickListener(this);
		findViewById(R.id.connect).setOnClickListener(this);
		findViewById(R.id.send).setOnClickListener(this);
		findViewById(R.id.disconnect).setOnClickListener(this);
		findViewById(R.id.cancel).setOnClickListener(this);
		findViewById(R.id.audio).setOnClickListener(this);
		findViewById(R.id.audio_test).setOnClickListener(this);
		findViewById(R.id.screen_clr).setOnClickListener(this);
		findViewById(R.id.audio_send).setOnClickListener(this);
		findViewById(R.id.text_display).setOnClickListener(this);
		mRadioGroup1 = (RadioGroup)findViewById(R.id.connect_type);
		
		//*
		Set<BluetoothDevice> set = mBtAdapter.getBondedDevices();
		for( BluetoothDevice device : set){
			mAddrList.add(device.getAddress());
		}
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mAddrList);
		Spinner spinner = (Spinner)findViewById(R.id.bt_address);
		spinner.setAdapter(adapter);
		spinner.setOnItemSelectedListener(new OnItemSelectedListener(){
			public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3){
				mRemoteAddr = mAddrList.get(arg2);
			}
			public void onNothingSelected(AdapterView<?> arg){				
			}
		});
		
		
		ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, uuids);
		Spinner spinner2 = (Spinner)findViewById(R.id.uuid_spinner);
		spinner2.setAdapter(adapter2);
		spinner2.setOnItemSelectedListener(new OnItemSelectedListener(){
			public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3){
				((EditText)findViewById(R.id.rfcomm_uuid)).setText(uuids[arg2]);
			}
			public void onNothingSelected(AdapterView<?> arg){				
			}
		});

    	mTimer = new Timer();
    	mTimer.schedule(mTimerTask, 10000, 10000);
    	
    
    	//String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/data/files/";
    	String path = "/sdcard/";
    	Log.i("RfcommTestActivity", path);
    	File fpath = new File(path);
		fpath.mkdirs();
		try{
			audioFile = new File(path+"/audio.pcm");
			audioFile.createNewFile();
			dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(audioFile)));
		}
		catch(Exception e){
			Log.e("RfcommTestActivity", "init", e);
		}
		//*/
	}
	
	public void onDestroy(){
		if( mRunThread != null ){
			mRunThread.cancel();
		}
		
		mAudioSend = false;
		//*
		try{
			dos.close();
		}
		catch(Exception e){
			Log.e("RfcommTestActivity", "init", e);
		}
		//*/
		mTimer.cancel();
		super.onDestroy();
	}
	
	public void ToggleAudioOutput(){
		mAudioOutput =  !mAudioOutput;
		printLine("Audio Output "+ (mAudioOutput ? "Enabled":"Disabled") + "\n");
		/*
		if( mAudioOutput ){
			mTrack = new Track();
			mTrack.start();
		}
		else{
			mTrack.cancel();
			mTrack = null;
			TrackList.clear();
		}
		//*/
	}
	public void ToggleAudioTest(){
		mAudioTest =  !mAudioTest;
		printLine("Audio Test "+ (mAudioTest ? "Enabled":"Disabled") + "\n");
	}
	
	public void ToggleAudioSend(){
		mAudioSend = !mAudioSend;
		printLine("Audio Send "+ (mAudioSend ? "Enabled":"Disabled") + "\n");
		if( mAudioSend ){
			if( mRecordPut == null ){
				mRecordPut = new RecordPut();
				mRecordPut.start();
			}
		}
		else{
			if( mRecordPut != null ){
				mRecordPut.stop();
				mRecordPut =  null;
			}
		}
	}
	
	public void ToggleTextDisplay(){
		mTextDisplay = !mTextDisplay;
		printLine("Text Display"+ (mTextDisplay ? "Enabled":"Disabled") + "\n");
	}
	
	public void printLine(String line){
		TextView tv = (TextView)findViewById(R.id.display_text);
		tv.append(line);
	}
	
	public void onClick(View view){
		switch(view.getId()){
		case R.id.listen:
			if( STATE_IDLE == mState ){
				mUuid = UUID.fromString(((EditText)findViewById(R.id.rfcomm_uuid)).getText().toString());
				channel_id = Integer.valueOf(((EditText)findViewById(R.id.rfcomm_channel)).getText().toString()).intValue();
				mState = STATE_LISTENING;	
				printLine("listen ...\n");	
				mRunThread = new RunThread();
				mRunThread.start();
			}
			break;
		case R.id.connect:
			if( STATE_IDLE == mState ){
				mUuid = UUID.fromString(((EditText)findViewById(R.id.rfcomm_uuid)).getText().toString());
				channel_id = Integer.valueOf(((EditText)findViewById(R.id.rfcomm_channel)).getText().toString()).intValue();
				mState = STATE_CONNECTING;
				printLine("connecting ...\n");
				mRunThread = new RunThread();
				mRunThread.start();
			}
			break;
		case R.id.send:
			if( STATE_CONNECTED == mState ){
				printLine("send ...\n");
				mRunThread.write((((EditText)findViewById(R.id.send_text)).getText().toString() + "\r").getBytes());
			}
			break;
		case R.id.disconnect:
			if( STATE_CONNECTED == mState ){
				mState = STATE_DISCONNECTING;
				printLine("disconnecting ...\n");
				mRunThread.disconnect();
				mRunThread = null;
			}
			break;
		case R.id.cancel:
			if( STATE_LISTENING == mState || STATE_CONNECTING == mState ){
				mState = STATE_IDLE;
				printLine("cancel...\n");
				mRunThread.cancel();
				mRunThread = null;
			}
			break;
		case R.id.audio:
			ToggleAudioOutput();
			break;
		case R.id.audio_test:
			ToggleAudioTest();
			break;
		case R.id.screen_clr:
			TextView tv = (TextView)findViewById(R.id.display_text);
			tv.setText("");
			break;
		case R.id.audio_send:
			ToggleAudioSend();
			break;
		case R.id.text_display:
			ToggleTextDisplay();
			break;
		}
	}
	
	static final int MSG_CONNECTED = 0;
	static final int MSG_ERROR = 1;
	static final int MSG_RECV = 2;
	static final int MSG_DISCONNECTED = 3;
	static final int MSG_STRING = 4;
	
	Handler mHandler = new Handler(){
		public void handleMessage(Message msg){
			switch(msg.what){
			case MSG_CONNECTED:
				mState = STATE_CONNECTED;
				printLine("connected\n");
				break;
			case MSG_ERROR:
				mState = STATE_IDLE;
				printLine(((Exception)msg.obj).getMessage());		
				printLine("\n");
				break;
			case MSG_RECV:
				printLine(Util.Bytes2String((byte[])msg.obj, msg.arg2));
				break;
			case MSG_DISCONNECTED:
				printLine("disconnected\n");
				break;
			case MSG_STRING:
				printLine((String)msg.obj);
				break;
			}
		}
	};
	
	static final int TRACK_BUF_SIZE = 160;
	static final int RECORD_BUF_SIZE = 320;
	static final int SEND_BUF_SIZE = RECORD_BUF_SIZE;
	
	class FillTrack extends Thread{
		private AudioTrack audioTrack;
		boolean temp;
		public FillTrack(AudioTrack track){
			audioTrack = track;
			temp = true;
		}
		public void run(){
			byte[] buffer = new byte[TRACK_BUF_SIZE];
			while(temp){
				audioTrack.write(buffer, 0, TRACK_BUF_SIZE);
				try{
					//Thread.sleep(TRACK_BUF_SIZE / 8);
				}
				catch(Exception e){
					
				}
			}
		}
		
		public void cancel(){
			temp = false;
		}
	}
	
	class RecordPut extends Thread{
		private AudioRecord audioRecord;
		private int minbuffersize;
		
		public void run(){			
			int sampleRateSelected = 8000;		
			int minbuffersize = AudioRecord.getMinBufferSize(sampleRateSelected, 
					channelConfiguration, audioEncoding);
			
			
			Log.i("VoiceTransfer", "Record buffersize="+minbuffersize + " samplerate="+sampleRateSelected);
			audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
					sampleRateSelected, channelConfiguration, audioEncoding,
					minbuffersize);
			audioRecord.startRecording();
			byte[] audioData = new byte[minbuffersize];
			byte[] encodeData = new byte[minbuffersize/4];
			byte[] sendBuf = new byte[SEND_BUF_SIZE];
			int offset = 0;
			while(mAudioSend){
				int bytes = audioRecord.read(audioData, 0, minbuffersize);
				record_count += bytes + 1;
				//int encodeBytes = VoiceCodec.OutputEncode(audioData, bytes, sampleRateSelected, encodeData, bytes/4);

				send_count += bytes + 1;
				
				int off1 = 0;
				while( bytes > 0 ){
					if( bytes >= SEND_BUF_SIZE - offset){
						System.arraycopy(audioData, off1, sendBuf, offset, SEND_BUF_SIZE);
						
						bytes -= SEND_BUF_SIZE;
						
						mRunThread.write(sendBuf, 0, SEND_BUF_SIZE);
						off1 += SEND_BUF_SIZE;
						offset = 0;
					}
					else{
						System.arraycopy(audioData, off1, sendBuf, offset, bytes);
						
						offset += bytes;
						bytes = 0;
					}
				}
	
				//mRunThread.write(encodeData, 0, encodeBytes);
				/*
				synchronized(RecorddataList){
					RecorddataList.add(audioData1);
					record_count += audioData1.length;
				}
				//*/
			}
		}
	}
	
	class RunThread extends Thread{
		private AudioTrack audioTrack;
		private FillTrack audioFill;
		
		InputStream mInStream;
		OutputStream mOutStream;
		boolean mConnected;
		public RunThread(){
			mConnected = false;
			audioTrack = null;
		}
		public void run(){
			try{				
				int sampleRateSelected = 8000;		
				int minbuffersize = AudioTrack.getMinBufferSize(sampleRateSelected, 
						channelConfiguration, audioEncoding);

				Log.i("VoiceTransfer", "Track buffersize="+minbuffersize + " samplerate="+sampleRateSelected);
				int sdk = Integer.parseInt(Build.VERSION.SDK);
				Log.i("VoiceTransfer", "sdk version:"+sdk);		        
				if( STATE_LISTENING == mState ){
					if (sdk >= 10) {
						mBtSrvSocket = mBtAdapter.listenUsingInsecureRfcommWithServiceRecord("Test", mUuid);
					}else{
						mBtSrvSocket = mBtAdapter.listenUsingRfcommWithServiceRecord("Test", mUuid);
					}
					mBtSocket = mBtSrvSocket.accept();
				}
				else if( STATE_CONNECTING == mState ){
					mBtDevice = mBtAdapter.getRemoteDevice(mRemoteAddr);
					if( mRadioGroup1.getCheckedRadioButtonId() == R.id.radioButton1 ){
						if (sdk >= 10) {
							mBtSocket = mBtDevice.createInsecureRfcommSocketToServiceRecord(mUuid);
						}else{
							mBtSocket = mBtDevice.createRfcommSocketToServiceRecord(mUuid);
						}
					}
					else if( mRadioGroup1.getCheckedRadioButtonId() == R.id.radioButton2){
						mBtSocket = BluetoothSocketFactory.createBluetoothSocket(
								BluetoothSocketFactory.TYPE_RFCOMM, 
								-1, 
								false, 
								false, 
								mBtDevice.getAddress(), 
								channel_id);
					}
					else{
						printLine("wrong value\n");
					}
					if( mBtSocket == null ){
						printLine("create socket failed\n");
						return;
					}
					mBtSocket.connect();
				}
				mInStream = mBtSocket.getInputStream();
				mOutStream = mBtSocket.getOutputStream();
				mHandler.obtainMessage(MSG_CONNECTED).sendToTarget();
				byte[] buffer = new byte[TRACK_BUF_SIZE/4];
				byte[] audioData = new byte[TRACK_BUF_SIZE];
				
	        	short[] audioTestData = new short[160];
				for( int i = 0; i < 160; ++i ){
					audioTestData[i] = (short)((i % 40) * 1024);
				}
				mConnected = true;
				int init = 0;
				while(mConnected){
					if( mAudioOutput){
						if( audioTrack == null ){
					        audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
									sampleRateSelected, channelConfiguration, audioEncoding,
									minbuffersize, AudioTrack.MODE_STREAM);
					        audioTrack.play();
					        audioFill = null;
					        if( !mAudioTest ){
					        	audioFill = new FillTrack(audioTrack);
					        	audioFill.start();
					        }
						}
						
						if( mAudioTest ){
							if( audioFill != null ){
								audioFill.stop();
								audioFill = null;
							}
							//TrackList.add(audioTestData);
							audioTrack.write(audioTestData, 0, 160);
						}
						else{
							int bytes = mInStream.read(buffer);
							
							recv_count += bytes + 1;
							
							VoiceCodec.InputDecode(buffer, bytes, 8000, audioData, bytes * 4);
							//System.arraycopy(buffer, 0, audioData, 0, bytes);
							//TrackList.add(audioData);
							if( audioFill != null ){
								audioFill.cancel();
								audioFill = null;
							}
							audioTrack.write(audioData, 0, bytes * 4);
							dos.write(audioData, 0, bytes * 4);

							track_count += bytes * 4 + 1;
						//Thread.sleep(20);
						}
					}
					else{
						if( audioTrack != null ){
							audioTrack.stop();
							audioTrack = null;
						}
						int bytes = mInStream.read(buffer);
						if( mTextDisplay )
							mHandler.obtainMessage(MSG_RECV, 0, bytes, buffer).sendToTarget();
					}
				}
				Log.i("RfcommTest", "Close");
				if( mBtSocket != null ){
					mBtSocket.close();
				}
				if( mBtSrvSocket != null ){
					mBtSrvSocket.close();
				}
				mHandler.obtainMessage(MSG_DISCONNECTED).sendToTarget();
			}
			catch(Exception e){
				mHandler.obtainMessage(MSG_ERROR, 0, 0, e).sendToTarget();
			}
		}
		
		public void write(byte[] buffer){
			try{
				mOutStream.write(buffer);
			}
			catch(Exception e){
				mHandler.obtainMessage(MSG_ERROR, 0, 0, e).sendToTarget();
			}
		}
		
		public void write(byte[] buffer, int offset, int size){
			try{
				mOutStream.write(buffer, offset, size);
			}
			catch(Exception e){
				mHandler.obtainMessage(MSG_ERROR, 0, 0, e).sendToTarget();
			}
		}
		
		public synchronized void disconnect(){
			try{
				mBtSocket.close();
				mBtSocket = null;
				mConnected = false;
			}
			catch(Exception e){
				
			}
		}
		
		public void cancel(){
			try{
				if( mBtSocket != null ){
					mBtSocket.close();
				}
				if( mBtSrvSocket != null ){
					mBtSrvSocket.close();
				}
			}
			catch(Exception e){
				
			}
		}
	};
	
	static final LinkedList<byte[]> TrackList = new LinkedList<byte[]>();

	static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
	static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
	
	/*
	class Track extends Thread{
		private AudioTrack audioTrack;
		boolean mCancel = false;
		public Track(){
			mCancel = false;
		}
		public void run(){
			int sampleRateSelected = 8000;		
			int minbuffersize = AudioTrack.getMinBufferSize(sampleRateSelected, 
					channelConfiguration, audioEncoding);
			

			Log.i("VoiceTransfer", "Track buffersize="+minbuffersize + " samplerate="+sampleRateSelected);
	        audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
					sampleRateSelected, channelConfiguration, audioEncoding,
					minbuffersize, AudioTrack.MODE_STREAM);
	        audioTrack.play();

        	//byte[] audioData = new byte[minbuffersize];
        	
        	//byte[] encodeData = new byte[160];
	        byte[] decodeData = new byte[160];
	        while(!mCancel){
	        	try{
					//int bytes = mInStream.read(encodeData);
	        		//int audioBytes = VoiceCodec.InputDecode(encodeData, bytes, sampleRateSelected, audioData, minbuffersize);

        			if( TrackList.size() > 2 ){
        				byte[] audioData = TrackList.removeFirst();
        				//VoiceCodec.InputDecode(audioData, audioData.length, 8000, decodeData, 160);
        				audioTrack.write(audioData, 0, audioData.length);
        				track_count += audioData.length;
        			}
	        	}
	        	catch(Exception e){
	        		
	        	}
	        }
		}
		public void cancel(){
			mCancel = true;
		}
	}
	//*/
}
